#ifndef __SAMPLES_PREPROCESSOR_H__
#define __SAMPLES_PREPROCESSOR_H__ 1

#include <stdint.h>
#include <pthread.h>

#include "engine_factory.h"
#include "audio_formats.h"
#include "codec.h"
#include "byte_ring_buffer.h"
#include "properties_reader.h"
#include "wave/wave_io.h"

#define MAX_SEND_INFO_COUNT 10000

class CSamplesPreprocessor
{
	public:
		CSamplesPreprocessor(NAudioFormats::SStreamInfo pIoStreamInfo,
				CStegCallHandle* pCallHandle,
				unsigned int pByteBufferSize,
				CPropertiesReader* pReader);
		virtual ~CSamplesPreprocessor();

		/* embeds data from the stego-framework into the given
		 * memory area; the result is a so called stego-object
		 * @param pStegoObject pointer to the memory area to modify
		 * @param pStegoObjectLength length of the memory area (unit:byte)
		 * @param pLeak (unit: byte)
		 * @return >0 success, 0 = EOF, <0 for error
		 */
		int createStegoObject(void* pStegoObject, unsigned int pStegoObjectLength, unsigned int pLeak);

		/* extracts data from the stego-framework from the given
		 * memory area
		 * @param pStegoObject: memory area to extract data from
		 * @param pStegoObjectLength: length of the memory area (unit:byte)
		 * @param pLeak (unit: byte)
		 * @return >= 0 skip in bytes
		 *         <0 error
		 */
		int extractFromStegoObject(void* pStegoObject, unsigned int pStegoObjectLength, unsigned int pLeak);

		inline void setExitForced() { mExitForced = true; }

		/**
		 * Convert from integer sample (S8, U8, S16LE, S16BE, S16HO, U16LE,
		 * U16BE, U16HO) to double (value between -1.0 to 1.0).
		 *
		 * @param pSamplesIn Integer input samples
		 * @param pSamplesOut Double output samples
		 * @param pSampleLength Count of samples. Count of bytes from pSamplesIn
		 *        can be calulated by pSampleLength * byte per sample.
		 * @param pIoStreamInfo Streaming infos for pSamplesIn
		 * @param pCodec Pointer to a codec instance which match with the
		 *        streaming infos.
		 * @return Count of converted samples ( = value of pSampleLength) or
		 *         0 for an error.
		 */
		static unsigned int convertFromIntToDouble(void* pSamplesIn,
				double* pSamplesOut,
				unsigned int pSampleLength,
				NAudioFormats::SStreamInfo &pIoStreamInfo,
				ICodec* pCodec);

		/**
		 * Convert from to double (value between -1.0 to 1.0) to
		 * integer sample (S8, U8, S16LE, S16BE, S16HO, U16LE, U16BE, U16HO).
		 *
		 * @param pSamplesIn Double input samples
		 * @param pSamplesOut Integer output samples
		 * @param pSampleLength Count of samples. Count of bytes from pSamplesOut
		 *        can be calulated by pSampleLength * byte per sample.
		 * @param pIoStreamInfo Streaming infos for pSamplesOut
		 * @param pCodec Pointer to a codec instance which match with the
		 *        streaming infos.
		 * @return Count of converted samples ( = value of pSampleLength) or
		 *         0 for an error.
		 */
		static unsigned int convertFromDoubleToInt(double* pSamplesIn,
				void* pSamplesOut,
				unsigned int pSampleLength,
				NAudioFormats::SStreamInfo &pIoStreamInfo,
				ICodec* pCodec);

		static unsigned int getBytePerSample(NAudioFormats::ESamplesWidth pSamplesWidth);

		static ICodec* newCodecPerCodecInfo(NAudioFormats::ECodec pCodecInfo);

	private:
		CSamplesPreprocessor(const CSamplesPreprocessor& );
		CSamplesPreprocessor& operator=(const CSamplesPreprocessor& );

		static const std::string sIniSection;

		bool mError;
		CPropertiesReader* mReader;
		bool mUseExtractThread; // default: true
		bool mOverwrite;
		CWaveIO* mWave;
		ICodec* mConvertCodec;

		class CExtractThread
		{
		public:
			CByteRingBuffer mRingBuffer;
			unsigned char* mEngineBuffer;
			unsigned int mEngineBufferSize;
			unsigned int mSendInfoCount;

			pthread_mutex_t mSync;
			pthread_cond_t mAvail;
			pthread_t mThreadId;

			enum EExitState
			{
				EXIT_STATE_NONE = 0,
				EXIT_STATE_NORMAL,
				EXIT_STATE_FORCED
			} mExit;

			CExtractThread(unsigned int pRingBufferSize, unsigned int pEngineBufferSize) :
					mRingBuffer(pRingBufferSize),
					mEngineBuffer(NULL),
					mEngineBufferSize(pEngineBufferSize),
					mSendInfoCount(MAX_SEND_INFO_COUNT),
					mExit(EXIT_STATE_NONE)
			{
				mEngineBuffer = new unsigned char [mEngineBufferSize];
				pthread_mutex_init(&mSync, NULL);
				pthread_cond_init(&mAvail, NULL);
			}

			virtual ~CExtractThread()
			{
				pthread_cond_destroy(&mAvail);
				pthread_mutex_destroy(&mSync);
				delete [] mEngineBuffer;
			}
		} mExtractThread;

		CStegCallHandle* mCallHandle;
		IEmbeddingEngine* mEmbeddingEngine;
		NAudioFormats::SStreamInfo mIoStreamInfo;
		ICodec* mCodec;
		double* mDoubleSampleBuf;
		int mDoubleSampleCount;
		double* mConvertDoubleSampleBuf;
		int mConvertDoubleSampleCount;
		int16_t* m16BitSampleBuf;
		int m16BitSampleCount;
		unsigned int mBytePerSample;
		bool mExitForced;

		double mAmpEmbed;
		unsigned int mAmpEmbedCount;
		double mAmpExtract;
		unsigned int mAmpExtractCount;

		/**
		 * @pLeak leak from last to this call
		 * @pInBuffer number of remaining samples
		 * @return (for pEmbedding = true): embed
		 *         0 for EOF
		 *         <0 for error
		 *         >0 for success
		 *         (for pEmbedding = false): extract
		 *         >= 0 for number of samples to skip for next call
		 *         <0 for error
		 */
		int embedOrExtract(void* pSamples, unsigned int pSampleLength, unsigned int pLeak, unsigned int pInBuffer, bool pEmbedding);

		/**
		 * @param pSampleLength Count of 16 bit samples. If pSampleLength <= 0
		 *        then the intern buffer will be freed.
		 */
		int16_t* get16BitSampleBuffer(int pSampleLength);

		/**
		 * @param pSampleLength Count of double samples. If pSampleLength <= 0
		 *        then the intern buffer will be freed.
		 */
		double* getDoubleBuffer(int pSampleLength);

		/**
		 * @param pSampleLength Count of double samples. If pSampleLength <= 0
		 *        then the intern buffer will be freed.
		 */
		double* getConvertDoubleBuffer(int pSampleLength);

		static int16_t switchEndian(int16_t pValue);
		static uint16_t switchEndian(uint16_t pValue);
		bool switchEndianFrom16BitBuffer(int16_t *pOutSamples,
				int16_t *pInSamples,
				int pSampleLength);
		bool switchEndianFrom16BitBuffer(uint16_t *pOutSamples,
				uint16_t *pInSamples,
				int pSampleLength);

		void addSamplesAmps(double& pAmp, unsigned int& pAmpCount, double* pSamples, unsigned int pSampleCount, bool pEmbedding);

		static void* extractThread(void* pThis);
		void* extractThread();
};

#endif /* __SAMPLES_PREPROCESSOR_H__ */

